# package deb

# import (
# 	"bytes"
# 	"fmt"
# 	"path/filepath"

# 	"github.com/aptly-dev/aptly/aptly"
# 	"github.com/aptly-dev/aptly/database"
# 	"github.com/ugorji/go/codec"
# )
import pickle
import database
from typing import List,Dict
from .package_files import *
from .formatp import *

from .package_deps import *
import os
# // PackageCollection does management of packages in DB
class PackageCollection:
    db:          database.Storage=None
    # codecHandle *codec.MsgpackHandle

    # // ByKey find package in DB by its key
    def ByKey(self,key:str) :
        encoded= self.db.Get(key)
        from deb import package 
        # DONE 循环引用了
        p = package.Package()
        if encoded is not None:
            # if len(encoded) > 2 and (encoded[0] != 0xc1 or encoded[1] != 0x1) :
            #     oldp = oldPackage()

            #     # decoder = codec.NewDecoderBytes(encoded, collection.codecHandle)
            #     # err = decoder.Decode(oldp)
            #     # if err is not None  {
            #     # 	return None, err
            #     # }

            #     p.Name = oldp.Name
            #     p.Version = oldp.Version
            #     p.Architecture = oldp.Architecture
            #     p.IsSource = oldp.IsSource
            #     p.SourceArchitecture = oldp.SourceArchitecture
            #     p.Source = oldp.Source
            #     p.Provides = oldp.Provides

            #     p.deps = PackageDependencies()
            #     p.deps.Depends=         oldp.Depends
            #     p.deps.BuildDepends=    oldp.BuildDepends
            #     p.deps.BuildDependsInDep= oldp.BuildDependsInDep
            #     p.deps.PreDepends=      oldp.PreDepends
            #     p.deps.Suggests=        oldp.Suggests
            #     p.deps.Recommends=      oldp.Recommends
                

            #     p.extra = oldp.Extra
            #     for i in oldp.Files :
            #         oldp.Files[i].Filename = os.path.basename(oldp.Files[i].Filename)
                
            #     p.UpdateFiles(PackageFiles(oldp.Files))

            #     # // Save in new format
            #     self.Update(p)
                
            # else :
                p=pickle.loads(encoded)
            #     err = decoder.Decode(p)
            #     if err is not None  {
            #         return None, err
            #     }
            # }

        p.collection = self

        return p

    # // loadExtra loads Stanza with all the xtra information about the package
    def  loadExtra(self,p ) ->Stanza :
        encoded= self.db.Get(p.Key("xE"))
        # if err is not None  {
        # 	raise ("unable to load extra")
        # }

        stanza = Stanza()
        stanza.data=encoded
        # decoder = codec.NewDecoderBytes(encoded, collection.codecHandle)
        # err = decoder.Decode(stanza)
        # if err is not None  {
        # 	raise ("unable to decode extra")
        # }

        return stanza
    

    # // loadDependencies loads dependencies for the package
    def loadDependencies(self,p ) ->PackageDependencies :
        encoded= self.db.Get(p.Key("xD"))
        # if err is not None  {
        # 	raise (fmt.Sprintf("unable to load deps: %s, %s", p, err))
        # }

        deps = PackageDependencies()
        deps=pickle.loads(encoded)
        # decoder = codec.NewDecoderBytes(encoded, collection.codecHandle)
        # err = decoder.Decode(deps)
        # if err is not None  {
        # 	raise ("unable to decode deps")
        # }

        return deps
    

    # // loadFiles loads additional PackageFiles record
    def loadFiles(self,p) ->PackageFiles :
        encoded= self.db.Get(p.Key("xF"))
        # if err is not None  {
        # 	raise ("unable to load files")
        # }

        files = PackageFiles()
        files=encoded
        # decoder = codec.NewDecoderBytes(encoded, collection.codecHandle)
        # err = decoder.Decode(files)
        # if err is not None  {
        # 	raise ("unable to decode files")
        # }

        return files
    

    # // loadContents loads or calculates and saves package contents
    def  loadContents(collection,p  , packagePool , progress  )->List[str]:
        encoded= collection.db.Get(p.Key("xC"))
        if encoded is not None :
            contents = pickle.loads(encoded)#string{}

            # decoder = codec.NewDecoderBytes(encoded, collection.codecHandle)
            # err = decoder.Decode(&contents)
            # if err is not None  :
            # 	raise ("unable to decode contents")
            # }

            return contents
        

        # if err != database.ErrNotFound {
        # 	raise ("unable to load contents")
        # }

        contents, err = p.CalculateContents(packagePool, progress)
        if err is not None  :
            # // failed to acquire contents, don't persist it
            return contents
        

        # var buf bytes.Buffer
        pick =pickle.dumps(contents)# codec.NewEncoder(&buf, collection.codecHandle).Encode(contents)
        # if err is not None  {
        # 	raise ("unable to encode contents")
        # }

        collection.db.Put(p.Key("xC"), pick)
        # if err is not None  {
        # 	raise ("unable to save contents")
        # }

        return contents
    

    # // Update adds or updates information about package in DB
    def  Update(collection,p ):
        
    # 	transaction, err = collection.db.OpenTransaction()
    # 	if err is not None  {
    # 		return err
    # 	}
    # 	defer transaction.Discard()

        collection.UpdateInTransaction(p)
    # 		return err
    # 	}

    # 	return transaction.Commit()
    # }

    # // UpdateInTransaction updates/creates package info in the context of the outer transaction
    def  UpdateInTransaction(collection,p ):
        # var encodeBuffer bytes.Buffer

        # encoder = codec.NewEncoder(&encodeBuffer, collection.codecHandle)

        # encodeBuffer.Reset()
        # encodeBuffer.WriteByte(0xc1)
        # encodeBuffer.WriteByte(0x1)
        # if err = encoder.Encode(p); err is not None  {
        # 	return err
        # }

        collection.db.Put(p.Key(""), pickle.dumps(p))
        # if err is not None  {
        # 	return err
        # }

        # // Encode offloaded fields one by one
        if p.files is not None  :
            # encodeBuffer.Reset()
            # err = encoder.Encode(*p.files)
            # if err is not None  {
            # 	return err
            # }

            collection.db.Put(p.Key("xF"), pickle.dumps(p.files))
        # 	if err is not None  {
        # 		return err
        # 	}
        # }

        if p.deps is not None  :
            # encodeBuffer.Reset()
            # err = encoder.Encode(*p.deps)
            # if err is not None  {
            #     return err
            # }

            collection.db.Put(p.Key("xD"), pickle.dumps(p.deps))
            # if err is not None  {
            #     return err
            # }

            # p.deps = None
        # }

        if p.extra is not None  :
            # encodeBuffer.Reset()
            # err = encoder.Encode(*p.extra)
            # if err is not None  {
            #     return err
            # }

            collection.db.Put(p.Key("xE"), pickle.dumps(p.extra))
            # if err is not None  {
            #     return err
            # }

            p.extra = None
        # }

        p.collection = collection

    #     return None
    # }

    # // AllPackageRefs returns list of all packages as PackageRefList
    # def (collection *PackageCollection) AllPackageRefs() *PackageRefList {
    # 	return &PackageRefList{Refs: collection.db.KeysByPrefix([]byte("P"))}
    # }

    # // DeleteByKey deletes package in DB by key
    # def (collection *PackageCollection) DeleteByKey(key []byte, dbw database.Writer) error {
    # 	for _, key = range [][]byte{key, append([]byte("xF"), key...), append([]byte("xD"), key...), append([]byte("xE"), key...)} {
    # 		err = dbw.Delete(key)
    # 		if err is not None  {
    # 			return err
    # 		}
    # 	}
    # 	return None
    # }

    # // Scan does full scan on all the packages
    # def (collection *PackageCollection) Scan(q PackageQuery) (result *PackageList) {
    # 	result = NewPackageListWithDuplicates(true, 0)

    # 	for _, key = range collection.db.KeysByPrefix([]byte("P")) {
    # 		pkg, err = collection.ByKey(key)
    # 		if err is not None  {
    # 			raise (fmt.Sprintf("unable to load package: %s", err))
    # 		}

    # 		if q.Matches(pkg) {
    # 			result.Add(pkg)
    # 		}
    # 	}

    # 	return
    # }

#     // Search is not implemented
# def (collection *PackageCollection) Search(_ Dependency, _ bool) (searchResults []*Package) {
# 	panic("Not implemented")
# }

    # // SearchSupported returns false
    # def (collection *PackageCollection) SearchSupported() bool {
    # 	return false
    # }

    # // SearchByKey finds package by exact key
    # def (collection *PackageCollection) SearchByKey(arch, name, version string) (result *PackageList) {
    # 	result = NewPackageListWithDuplicates(true, 0)

    # 	for _, key = range collection.db.KeysByPrefix([]byte(fmt.Sprintf("P%s %s %s", arch, name, version))) {
    # 		pkg, err = collection.ByKey(key)
    # 		if err is not None  {
    # 			raise (fmt.Sprintf("unable to load package: %s", err))
    # 		}

    # 		if pkg.Architecture == arch && pkg.Name == name && pkg.Version == version {
    # 			result.Add(pkg)
    # 		}
    # 	}

    # 	return
    # }



# // Verify interface
# var (
# 	_ PackageCatalog = &PackageCollection{}
# )

# // NewPackageCollection creates new PackageCollection and binds it to database
def NewPackageCollection(db :database.Storage) ->PackageCollection :
    packageCollection=PackageCollection()
    packageCollection.db=db
    return packageCollection

# // oldPackage is Package struct for aptly < 0.4 with all fields in one struct
# // It is used to decode old aptly DBs
class oldPackage :
    IsSource    :       bool=None
    Name         :str=''
    Version       :str=''
    Architecture   :str=''
    SourceArchitecture:str=''
    Source            :str=''
    Provides           :List[str]=None
    Depends            :List[str]=None
    BuildDepends       :List[str]=None
    BuildDependsInDep  :List[str]=None
    PreDepends         :List[str]=None
    Suggests           :List[str]=None
    Recommends         :List[str]=None
    Files              :List[PackageFile]=None
    Extra              :Stanza=None

